package system

import (
	"context"
	"encoding/json"
	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"runtime/debug"
	"server/internal/consts"
	"server/internal/model"
	"server/internal/service"
	"strconv"
	"time"
)

// 利用redis针对用户的权限数据进行缓存，接口请求验证时直接读取缓存数据，减少对数据库查询权限相关数据的次数，因此需要缓存用户获取权限数据的时间和系统修改权限相关数据的时间，用于对比用户是否需要更新缓存数据
// redis.hash(CacheHashUserPermissionReadTime) 表记录系统权限相关数据修改的时间（系统权限修改时间，字段存储为 system，值为读取的当前时间）
// redis.hash(CacheHashUserPermissionReadTime) 表记录用户权限读取后缓存redis的时间（用户权限缓存时间，字段存储为 用户id，值为读取的当前时间）
// redis.hash(CacheHashUserPermissionData) 表记录用户权限最新的缓存数据

type sSystemAuthorityCache struct {
}

func init() {
	service.RegisterSystemAuthorityCache(&sSystemAuthorityCache{})
}

// SetSystemPermissionLastUpdatedTime 系统权限相关数据修改时间
func (s *sSystemAuthorityCache) SetSystemPermissionLastUpdatedTime(ctx context.Context) {
	_, err := g.Redis().HSet(ctx, consts.CacheHashUserPermissionReadTime, g.Map{
		"system": time.Now().Format(consts.CacheAuthTimeFormat),
	})
	if err != nil {
		g.Log().Error(ctx, err)
	}
}

// GetSystemPermissionLastUpdatedTime 用户最近一次读取权限数据的缓存时间
func (s *sSystemAuthorityCache) GetSystemPermissionLastUpdatedTime(ctx context.Context) int64 {
	res, err := g.Redis().HGet(ctx, consts.CacheHashUserPermissionReadTime, "system")
	if err != nil {
		g.Log().Error(ctx, err)
		return 0
	}
	if len(res.String()) == 0 {
		return 0
	}
	if t, err := time.ParseInLocation(consts.CacheAuthTimeFormat, res.String(), time.Local); err != nil {
		g.Log().Error(ctx, err)
		return 0
	} else {
		return t.UnixMicro()
	}
}

// SetUserPermissionReadTime 设置用户最近一次读取权限数据的缓存时间
func (s *sSystemAuthorityCache) SetUserPermissionReadTime(ctx context.Context, adminId int64) error {
	_, err := g.Redis().HSet(ctx, consts.CacheHashUserPermissionReadTime, g.Map{
		strconv.Itoa(int(adminId)): time.Now().Format(consts.CacheAuthTimeFormat),
	})
	if err != nil {
		g.Log().Error(ctx, err)
		return err
	}
	return nil
}

// GetUserPermissionReadTime 获取用户最近一次读取权限数据的缓存时间
func (s *sSystemAuthorityCache) GetUserPermissionReadTime(ctx context.Context, adminId int64) int64 {
	res, err := g.Redis().HGet(ctx, consts.CacheHashUserPermissionReadTime, strconv.Itoa(int(adminId)))
	if err != nil {
		g.Log().Error(ctx, err)
		return 0
	}
	if len(res.String()) == 0 {
		return 0
	}
	if t, err := time.ParseInLocation(consts.CacheAuthTimeFormat, res.String(), time.Local); err != nil {
		g.Log().Error(ctx, err)
		return 0
	} else {
		return t.UnixMicro()
	}
}

// RemoveUserPermissionLastUpdatedTime 删除用户最近一次读取权限数据的缓存时间
func (s *sSystemAuthorityCache) RemoveUserPermissionLastUpdatedTime(ctx context.Context, adminId int64) {
	_, err := g.Redis().HDel(ctx, consts.CacheHashUserPermissionReadTime, strconv.Itoa(int(adminId)))
	if err != nil {
		g.Log().Error(ctx, err)
	}
}

// CheckAdminPermissionIsExpire 用户获取权限数据的时间和系统修改权限相关数据的时间，用于对比用户是否需要更新缓存数据
func (s *sSystemAuthorityCache) CheckAdminPermissionIsExpire(ctx context.Context, adminId int64) bool {
	//系统权限相关数据最后的修改时间
	t1 := s.GetSystemPermissionLastUpdatedTime(ctx)
	if t1 == 0 {
		return false //系统时间未设置说明权限没有修改过
	}

	//当前用户的权限保存时间
	t2 := s.GetUserPermissionReadTime(ctx, adminId)
	if t2 == 0 {
		return true //未获取到用户缓存时间则说明第一次请求
	}
	return t1 >= t2
}

// SetUserCacheAuthData 用户的基本信息和权限数据缓存
func (s *sSystemAuthorityCache) SetUserCacheAuthData(ctx context.Context, userId int64) error {
	//读取权限数据
	authData, err := service.SystemAuthority().GetUserAuthData(ctx, userId)
	if err != nil {
		g.Log().Error(ctx, err.Error())
		return err
	}

	//用户数据写入hash表
	_, err = g.Redis().HSet(ctx, consts.CacheHashUserPermissionData, g.Map{
		strconv.Itoa(int(userId)): authData,
	})
	if err != nil {
		return err
	}

	//设置设置缓存数据的时间
	err = s.SetUserPermissionReadTime(ctx, authData.Id)

	return err
}

// RemoveUserCacheAuthData 删除用户缓存的权限数据
func (s *sSystemAuthorityCache) RemoveUserCacheAuthData(ctx context.Context, userId int64) {
	_, _ = g.Redis().HDel(ctx, consts.CacheHashUserPermissionData, strconv.Itoa(int(userId)))
	s.RemoveUserPermissionLastUpdatedTime(ctx, userId)
}

// GetUserCacheAuthData 获取登录用户的缓存数据
func (s *sSystemAuthorityCache) GetUserCacheAuthData(ctx context.Context) (adminAuthData *model.SystemUserAuthData, err error) {
	adminAuthData = new(model.SystemUserAuthData)
	defer func() {
		if r := recover(); r != nil {
			g.Log().Infof(ctx, "getUserCacheAuthData err:%+v, stack:%+v", r, string(debug.Stack()))

		}
	}()

	adminID := service.SystemLogin().GetAdminID(ctx)
	if adminID == 0 {
		return adminAuthData, gerror.New("用户信息解析失败")
	}

	//判断是否需要刷新缓存数据后重新获取
	if s.CheckAdminPermissionIsExpire(ctx, adminID) {
		g.Log().Infof(ctx, "[%d] refresh userCacheAuthData", adminID)
		if err = s.SetUserCacheAuthData(ctx, adminID); err != nil {
			return adminAuthData, err
		}

	}

	//查询缓存数据
	data, err := g.Redis().HGet(ctx, consts.CacheHashUserPermissionData, strconv.Itoa(int(adminID)))
	if err != nil {
		return adminAuthData, err
	}
	if err = json.Unmarshal(data.Bytes(), adminAuthData); err != nil {
		return adminAuthData, err
	}

	return adminAuthData, err
}
